<template>
  <div class="executor-main">
    <el-container class="container-item" direction="vertical" style="padding: 0;">
      <!-- 历史记录 -->
      <div v-for="(item, index) in history" :key="index" class="container-item">
        <!-- 元芳的回答 -->
        <div v-if="item.speaker === 'robot'">
          <!--          <div style="overflow: hidden;">-->
          <div v-if="item.notDisplayAvatar !=='none'" style="overflow: hidden;">
            <img
              ref="iconImg2"
              :src="img_src"
              alt="元芳"
              style="float: right"
              class="yuanfang-avatar"
            >
          </div>
          <div style="clear: both;" />
          <div class="content-wrapper dialog-box">
            <div v-if="item.type === 'text'">
              <!--              <span v-html="replaceNewlinesWithBr(item.data)" />-->
              <span v-html="item.data" />
            </div>
            <div v-else-if="item.type === 'file'" :id="`file-${index}`" />
            <div v-else-if="item.type === 'echart'" :id="`chart-${index}`" style="height: 500px;">
              222
            </div>
            <div v-else-if="item.type === 'video'" :id="`video-${index}`" />
            <div v-else-if="item.type === 'error-message'" :id="`error-message-${index}`">
              哎呀，看起来元芳遇到了个小坎坷！
              <a @click="toggleErrorLog(item)">[错误日志]</a>
              <div v-if="item.showError">{{ item.data }}
              </div>
            </div>
            <div v-else-if="item.type === 'stop-app'" :id="`stop-app-${index}`">
              {{ item.data }}
            </div>
          </div>
        </div>
        <!-- 用户的提问及输入 -->
        <div v-else>
          <img :src="require(`@/assets/avatars/${avatarIndex}.jpg`)" alt="User" class="user-avatar">
          <div class="content-wrapper dialog-box">
            <!-- input输入 -->
            <div v-if="item.type === 'input'">
              <div v-for="(text, i) in item.data" :key="i" :class="{ 'item-spacing': i !== 0 }">
                <span v-html="text.replace(/\n/g, '<br>')" />
                <!--                <span v-html="text" />-->
              </div>
            </div>
            <!-- 文件输入 -->
            <div v-else-if="item.type === 'file'" :id="`input-file-${index}`">

              <!--              <img :src="getOringalImg(item.file)" alt="Uploaded Image" style="maxWidth: 100%;" class="responsive-image">-->
              <!--              <br>-->
              已上传成功
              <a :href="downloadUrl(item.file)" :download="item.file.name" class="download-link">下载</a>
            </div>
          </div>
        </div>

      </div>

      <!-- 展示用户的输入框 -->
      <el-container>
        <div v-if="inputDialogVisible" class="container-item focus-bottom">
          <div style="overflow: hidden;">
            <img
              ref="iconImg1"
              :src="img_src"
              alt="元芳"
              style="float: right"
              class="yuanfang-avatar"
            >
          </div>
          <div style="clear: both;" />
          <div>
            <el-form class="content-wrapper dialog-box" @submit.native.prevent="submitForm">
              <div v-for="(arg, argIndex) in step.userInput" :key="argIndex">
                <div v-if="arg.sourceType === 'input'">
                  <template v-if="arg.sourceData.type === 'singleSelect'">
                    <el-form-item :label="arg.sourceData.option.prompt">
                      <br>
                      <div v-for="(option, optionIndex) in arg.sourceData.option.options" :key="optionIndex">
                        <el-radio v-model="arg.sourceData.option.defaultValue" :label="option.value">{{ option.name }}</el-radio>
                      </div>
                    </el-form-item>
                  </template>
                  <template v-else-if="arg.sourceData.type === 'multiSelect'">
                    <el-form-item :label="arg.sourceData.option.prompt">
                      <br>
                      <el-checkbox-group v-model="selectedOptions[argIndex]">
                        <el-checkbox v-for="(option, optionIndex) in arg.sourceData.option.options" :key="optionIndex" :label="option.value" @change="handleMultiSelect">
                          {{ option.name }}
                        </el-checkbox>
                      </el-checkbox-group>
                    </el-form-item>
                  </template>
                  <template v-else-if="arg.sourceData.type === 'editBox'">
                    <el-form-item :label="arg.sourceData.option.prompt">
                      <el-input
                        v-if="arg.sourceData.option.inputMode === 'singleLine'"
                        v-model="arg.sourceData.option.defaultValue"
                        v-focus="argIndex === 0"
                        @keyup.enter="submitForm"
                      />
                      <el-input
                        v-if="arg.sourceData.option.inputMode === 'multiLine'"
                        v-model="arg.sourceData.option.defaultValue"
                        v-focus="argIndex === 0"
                        type="textarea"
                        :autosize="{ minRows: 1, maxRows: 1000 }"
                        @keyup.enter="submitForm"
                      />
                      <el-input
                        v-if="arg.sourceData.option.inputMode === 'password'"
                        v-model="arg.sourceData.option.defaultValue"
                        v-focus="argIndex === 0"
                        type="password"
                        @keyup.enter="submitForm"
                      />
                    </el-form-item>
                  </template>
                </div>

                <div v-else-if="arg.sourceType === 'file'">
                  <el-form-item>
                    <div style="margin-bottom: 5px; font-weight: bold; font-size: 16px;">{{ arg.sourceData.option.prompt }}</div>
                    <el-upload
                      :http-request="(param) => uploadFile(param, arg.sourceData.option.variableName)"
                      :before-upload="(file) => beforeUpload(file, arg.sourceData.option.fileSizeLimit, arg.sourceData.option.fileSizeExtension)"
                      :before-remove="beforeRemoveFile"
                      drag
                      :limit="10"
                      :on-success="(response, file, fileList) => handleSuccess(response, file, fileList, argIndex)"
                      :on-exceed="handleExceed"
                      :file-list="fileLists[argIndex]"
                      style="margin-top: 15px"
                    >
                      <i class="el-icon-upload" />
                      <div class="el-upload__text">{{ $t('Drag_file_tips') }}<em>{{ $t('click') + $t('upload') }}</em>
                      </div>
                      <div slot="tip" class="el-upload__tip">只能上传 {{ arg.sourceData.option.fileSizeExtension }} 文件，且文件大小不超过{{ arg.sourceData.option.fileSizeLimit }}kB</div>
                    </el-upload>
                    <el-row v-if="on_uploading" type="flex" justify="center">
                      <el-col>
                        <el-progress :percentage="upload_percentage" />
                      </el-col>
                    </el-row>
                  </el-form-item>
                </div>

              </div>
              <el-form-item style="text-align: center">
                <el-button type="primary" @click="submitForm">{{ $t('submit') }}</el-button>
                <el-button @click="cancelExecution">{{ $t('cancel') }}</el-button>
              </el-form-item>
            </el-form>
          </div>
        </div>
      </el-container>

      <!-- 思考中 -->
      <el-container v-if="waitingTaskList && !executionCompleted">
        <div class="container-item focus-bottom">
          <!-- 思考中的主体 -->
          <div class="content-wrapper dialog-box">
            <div
              v-loading="waitingTaskList"
              :element-loading-text="$t('appThinking')"
              element-loading-spinner="el-icon-loading"
              style="padding: 10px; height: 40px"
            />
          </div>
        </div>
      </el-container>

      <el-container>
        <div class="container-item">
          <div class="button-container">
            <!-- 重新执行等操作按钮 -->
            <el-button v-if="executionCompleted || errorMessage" @click="restartExecution">{{ $t('re_run') }}</el-button>
            <!-- 退出应用按钮 -->
            <el-button @click="toDetail">{{ $t('back_to_detail') }}</el-button>
            <!-- 查看日志按钮 -->
            <el-button @click="toLog">{{ $t('view_log') }}</el-button>
            <!-- 编辑应用按钮 -->
            <el-button @click="toEdit">{{ $t('edit_app') }}</el-button>
          </div>
        </div>
      </el-container>
    </el-container>
  </div>
</template>
<script>
import { executeAppStep, getAppDetail, getExecutroMemoryBus } from '@/api/executor'
import * as echarts from 'echarts'
import { uploadFile } from '@/api/executor'
import Autosize from 'vue-autosize'
import Hls from 'hls.js'
import React, { useRef, useState, useEffect } from 'react'
import { getIconByAppId } from '@/api/app'
import item from '@/layout/components/Sidebar/Item.vue'

export default {
  name: 'NewExe',
  directives: {
    autosize: Autosize
  },
  data() {
    return {
      // 应用信息
      appId: 0,
      appName: null,
      originalAppDetail: null,

      memoryBus: null,
      inputDialogVisible: false,
      waitingTaskList: false,
      errorMessage: false,
      selectedOptions: {}, // 记录复选框的值

      // 执行信息
      nowOnTask: 0,
      nowTaskContent: null,
      nowTaskArgs: null,
      args: null,
      step: null,
      appDetail: null,

      /* 上传文件相关 */
      fileLists: {},
      // fileList: [],
      file: null,
      fileData: null,
      formData: null,
      img: null,
      downloadLink: null,
      fileName: [],
      fileMap: {}, // 用于存储 variableName 和 newFileName 的映射
      removeConfirm: true, // 文件格式大小不符合要求时,取消删除文件的提示
      on_uploading: false,
      upload_percentage: 0,
      // fileName: null,

      history: [],
      avatarIndex: 0,
      notDisplayAvatar: '',
      renderHistoryIndex: 0, // 渲染history索引

      // 图片加载
      img_src: null
    }
  },
  computed: {
    notRunToEnd() {
      return this.nowOnTask < this.appDetail.script.length
    },
    executionCompleted() {
      return this.appDetail.script && this.nowOnTask >= this.appDetail.script.length
    }
  },
  watch: {
    history: {
      immediate: true,
      handler(newArray) {
        // console.log('数组：', newArray)
        if (newArray.length > this.renderHistoryIndex) {
          // 遍历从 renderHistoryIndex 到 newArray.length - 1 的部分
          for (let i = this.renderHistoryIndex; i < newArray.length; i++) {
            const item = newArray[i]
            if (item.type === 'echart') {
              this.$nextTick(() => {
                const chartDom = document.querySelector(`#chart-${i}`)
                const myChart = echarts.init(chartDom)
                myChart.setOption(item.data)
              })
            } else if (item.type === 'file') {
              this.$nextTick(() => {
                const fileDom = document.querySelector(`#file-${i}`)
                if (fileDom) {
                  // 展示文件结果
                  this.createImageFromBinaryStream(item.data)
                  fileDom.style.display = 'block' // 显示文件
                  fileDom.appendChild(self.img) // 显示图片
                  var spaceNode = document.createElement('span')
                  spaceNode.innerHTML = '&nbsp;&nbsp;'
                  fileDom.appendChild(spaceNode)
                  fileDom.style.whiteSpace = 'normal'
                  fileDom.appendChild(self.downloadLink) // 显示链接
                }
              })
            } else if (item.type === 'video') {
              this.$nextTick(() => {
                const videoDom = document.querySelector(`#video-${i}`)

                if (videoDom) {
                  // 创建 video 元素
                  const videoElement = document.createElement('video')
                  videoElement.controls = true // 显示控制条
                  videoElement.autoplay = true // 自动播放
                  videoElement.style.width = '100%' // 设置视频宽度
                  // 将 video 元素添加到 DOM 中
                  videoDom.appendChild(videoElement)

                  // 使用 Hls.js 处理 HLS 流
                  if (Hls.isSupported()) {
                    const hls = new Hls()

                    // // 设置 CORS 属性（确保 HLS 请求支持跨域）
                    // hls.config.xhrSetup = function(xhr, url) {
                    //   xhr.withCredentials = true // 如果需要携带凭证
                    //   xhr.setRequestHeader('Access-Control-Allow-Origin', '*') // 允许跨域
                    // }

                    console.log('11111', item.data)
                    hls.loadSource(item.data) // 加载 HLS URL
                    hls.attachMedia(videoElement) // 绑定 video 元素

                    hls.on(Hls.Events.MANIFEST_PARSED, () => {
                      videoElement.play() // 自动播放
                    })

                    // 清理函数
                    hls.on(Hls.Events.ERROR, (event, data) => {
                      if (data.fatal) {
                        console.log('7777777"', data.type)
                        switch (data.type) {
                          case Hls.ErrorTypes.NETWORK_ERROR:
                            console.error('Fatal network error encountered, try to recover')
                            hls.startLoad()
                            break
                          case Hls.ErrorTypes.MEDIA_ERROR:
                            console.error('Fatal media error encountered, try to recover')
                            hls.recoverMediaError()
                            break
                          default:
                            hls.destroy()
                            break
                        }
                      }
                    })
                  } else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
                    alert('222222')
                    // Safari 原生支持 HLS
                    videoElement.src = item.data
                    videoElement.addEventListener('loadedmetadata', () => {
                      videoElement.play() // 自动播放
                    })
                  } else {
                    alert('333333')
                    console.error('HLS is not supported in this browser.')
                  }
                }
              })
            }
          }
          // 更新 renderHistoryIndex 为当前 history 的长度
          this.renderHistoryIndex = newArray.length
        }
      }
    },
    nowOnTask: {
      handler(newVal) {
        if (this.appDetail.script && newVal >= this.appDetail.script.length) {
          this.history.push({
            'speaker': 'robot',
            'type': 'text',
            'data': `"${this.appName}"${this.$t('finished_running')}`
          })
        }
      }
    },
    // 当出现思考中时，滚动到思考中处
    waitingTaskList: {
      handler(newVal) {
        if (newVal) {
          this.$nextTick(() => {
            this.scrollToInput()
          })
        }
      }
    },
    errorMessage: {
      handler(newVal) {
        if (newVal) {
          this.$nextTick(() => {
            this.scrollToCompletion()
          })
        }
      }
    }
  },
  created() {
    this.avatarIndex = parseInt(this.$store.getters.avatar) + 1
    this.appId = this.$route.query.id
    this.loadIcon()
    this.getDetailAndShow()
  },
  // mounted() {
  //   this.loadIcon2()
  // },
  methods: {
    async getDetailAndShow() {
      getAppDetail(this.appId, this.$store.getters.id).then(res => {
        console.log('获取到的应用详情:', res)
        this.appDetail = res
        this.appDetail.script = JSON.parse(this.appDetail.script)
        this.originalAppDetail = JSON.parse(JSON.stringify(this.appDetail))
        this.appName = res.app_name
        this.history.push({
          'speaker': 'user',
          'type': 'input',
          'data': [`<strong>${this.$t('launch')}${this.$t('application')}:</strong> ${this.appName}`]
        })
        // 获取图片ID
        this.imageId = this.getImageId(this.appName)
        this.autoFillArgs(this.nowOnTask)
      }).catch(error => {
        console.log('执行时获取详情出错:', error)
      })
    },

    replaceNewlinesWithBr(text) {
      var tempText = text
      return tempText.replace(/\n/g, '<br>')
    },
    async autoFillArgs(task) {
      this.nowOnTask = task
      this.nowTaskContent = this.appDetail.script.at(task)
      this.nowTaskArgs = []
      this.args = this.nowTaskContent.parameters
      this.selectedOptions = {}
      await getExecutroMemoryBus().then((res) => {
        this.memoryBus = res
        console.log(res)
      }).catch(error => {
        console.log('子组件填参', error)
      })

      // 初始化input的value，展示正确的默认值
      this.args.forEach((arg, index) => {
        if (arg.sourceType === 'input' && arg.sourceData.type === 'editBox') {
          // 如果该值为profile.开头，则默认其为使用profile中的变量；如果没取到对应变量，则不进行改变
          try {
            if (arg.sourceData.option.defaultValue.startsWith('profile')) {
              const keys = arg.sourceData.option.defaultValue.split('.')
              let current = this.memoryBus
              for (const key of keys) {
                current = current[key]
              }
              arg.sourceData.option.defaultValue = current
            }
          } catch (e) {
            console.log(e)
          }
        } else if (arg.sourceType === 'input' && arg.sourceData.type === 'singleSelect') {
          for (let i = 0; i < arg.sourceData.option.options.length; i++) {
            if (arg.sourceData.option.options[i].checked === 'true') {
              this.$set(arg.sourceData.option, 'defaultValue', arg.sourceData.option.options[i].value)
              break
            }
          }
        } else if (arg.sourceType === 'input' && arg.sourceData.type === 'multiSelect') {
          this.selectedOptions[index] = []
          arg.sourceData.option.defaultValue = ''
        }
      })

      const hasInputArgs = this.args.some(arg => arg.sourceType === 'input') || this.args.some(arg => arg.sourceType === 'file')
      this.step = {
        userInput: this.args,
        task_id: this.nowOnTask + 1,
        canceled: false
      }
      if (hasInputArgs) {
        this.inputDialogVisible = true
      } else {
        await this.submitForm()
      }
      this.$nextTick(() => {
        this.scrollToInput()
      })
    },
    async submitForm() {
      try {
        console.log(this.fileName)
        if (this.fileLists) {
          // 文件名
          // this.appDetail.file = this.fileData
          this.appDetail.file = this.fileName
        }

        // this.memoryBus = await getExecutroMemoryBus()

        try {
          this.memoryBus = await getExecutroMemoryBus()
        } catch (error) {
          console.log('执行单步task失败：', error.response?.data)
          this.errorMessage = true
        }

        // 初始化一个计数器，用于记录文件名填充到第几个文件
        const fileIndex = 0
        this.args.forEach((arg, index) => {
          if (arg.sourceType === 'refer') {
            const path = arg.sourceData.option.defaultValue.split('.')
            let value = this.memoryBus
            console.log('memoryBus:---', value)
            // 如果路径的第一个部分是 'codeVar'，则跳过
            if (path[0] === 'codeVar') {
              path.shift() // 移除 'codeVar'
            }

            for (let i = 0; i < path.length; i++) {
              value = value[path[i]]
              if (value === undefined) break
            }
            arg.sourceData.option.defaultValue = value
            arg.sourceData.option.variableName = arg.name
          } else if (arg.sourceType === 'input' && arg.sourceData.type === 'singleSelect') {
            arg.sourceData.option.name = arg.sourceData.option.options.find(item => item.value === arg.sourceData.option.defaultValue).name
          } else if (arg.sourceType === 'input' && arg.sourceData.type === 'multiSelect') {
            // console.log('获取到的多选值', this.selectedOptions)
            arg.sourceData.option.defaultValue = this.selectedOptions[index].join(';')
          } else if (arg.sourceType === 'file') {
            // 检查 fileMap 中是否存在对应的 variableName
            if (this.fileMap && this.fileMap[arg.sourceData.option.variableName]) {
              // 如果存在对应的文件名，则将该文件名填充到 defaultValue 中
              arg.sourceData.option.defaultValue = this.fileMap[arg.sourceData.option.variableName]
            } else {
              // 如果 fileMap 中没有对应的文件名，则填充空值或提示
              arg.sourceData.option.defaultValue = ''
            }
            // // 如果 fileName 数组中有文件名，则填充到 defaultValue 中
            // if (this.fileName && this.fileName.length > fileIndex) {
            //   arg.sourceData.option.defaultValue = this.fileName[fileIndex]
            //   fileIndex++ // 移动到下一个文件名
            // } else {
            //   // 如果 fileName 数组中没有足够的文件名，则填充空值或提示
            //   arg.sourceData.option.defaultValue = ''
            // }
          }
        })

        // 修改参数为真正提交的
        // this.args.reduce((acc, arg) => {
        //   if (arg.sourceType === 'input') {
        //     if (arg.input.inputType === 'editBox') {
        //       acc[arg.name] = arg.value
        //     } else if (arg.input.inputType === 'singleSelect') {
        //       acc[arg.name] = arg.value
        //     } else if (arg.input.inputType === 'multiSelect') {
        //       arg.value = this.selectedOptions.join(';')
        //       acc[arg.name] = arg.value
        //     }
        //   } else if (arg.sourceType === 'const') {
        //     acc[arg.name] = {
        //       value: arg.value,
        //       type: 'Const'
        //     }
        //   } else if (arg.sourceType === 'refer') {
        //     acc[arg.name] = {
        //       value: arg.value,
        //       type: 'Refer'
        //     }
        //   }
        //   if (arg.sourceType === 'file') {
        //     arg.value = this.fileName
        //     // console.log('3333', arg.value)
        //   }
        //   return acc
        // }, {})

        const inputArgs = []
        for (let i = 0; i < this.args.length; i++) {
          // console.log('参数：', this.args[i])
          if (this.args[i].sourceType === 'file') {
            this.history.push({
              'speaker': 'user',
              'type': 'file',
              'filename': this.args[i].value,
              'fileData': this.fileData,
              'file': this.file,
              'description': this.args[i].description
            })
          } else if (this.args[i].sourceType === 'input') {
            if (this.args[i].sourceData.option.inputMode === 'password') {
              const defaultValue = this.args[i].sourceData.option.defaultValue
              const maskedValue = '*'.repeat(defaultValue.length) // 根据字符数生成相同数量的 *
              inputArgs.push(this.args[i].sourceData.option.prompt + '  ' + maskedValue)
            } else if (this.args[i].sourceData.type === 'singleSelect') {
              inputArgs.push(this.args[i].sourceData.option.prompt + '  ' + this.args[i].sourceData.option.name)
            } else {
              inputArgs.push(this.args[i].sourceData.option.prompt + '  ' + this.args[i].sourceData.option.defaultValue)
            }
          }
        }
        if (inputArgs.length !== 0) {
          this.history.push({
            'speaker': 'user',
            'type': 'input',
            'data': inputArgs
          })
        }

        if (this.inputDialogVisible) {
          this.inputDialogVisible = false
        }

        if (!this.waitingTaskList) {
          this.waitingTaskList = true
        }
        console.log('执行前的', this.appDetail)
        // Process next step
        await executeAppStep({ 'appDetail': this.appDetail, 'id': this.$store.getters.id }).then(async response => {
          console.log('单步执行task：', response)
          await this.showChart()
          if (this.notRunToEnd) {
            this.autoFillArgs(this.nowOnTask)
          }
        }).catch(error => {
          let errorMessage = ''
          let flag = false // 判断是否是跳转或者中止执行
          if (this.waitingTaskList) {
            this.waitingTaskList = false
          }
          if (!error.response.data.message) {
            errorMessage = '未知错误'
          } else {
            errorMessage = error.response.data.message
            // 胶水代码用户中止执行，就会执行if, 跳转就会执行else if
            if (error.response.data.message === 'error to execute: Aborting the current program') {
              this.$message.warning('The user has aborted the current program')
              errorMessage = '用户主动中止当前应用'
              flag = true
            } else if (error.response.data.message.startsWith('error to execute: Error in user function: Skip to step ')) {
              this.appDetail = JSON.parse(JSON.stringify(this.originalAppDetail))
              // 获取用户要跳转的步骤
              const step = parseInt(error.response.data.message.slice(55))
              this.nowOnTask = step - 1
              this.autoFillArgs(this.nowOnTask)
              return true
            } else if (error.response.data.message.startsWith('error to execute: Error in user function: Skip back to step ')) {
              this.appDetail = JSON.parse(JSON.stringify(this.originalAppDetail))
              // 获取用户要向后跳转的步骤
              const step = parseInt(error.response.data.message.slice(60))
              this.nowOnTask = step - 1
              this.autoFillArgs(this.nowOnTask)
              return true
            } else if (error.response.data.message.startsWith("error to execute: 'NoneType' object has no attribute 'memoryBus'")){
              errorMessage = '由于您长时间未操作，应用已自动退出。如需继续使用，请再次运行此应用。'
              flag = true
            }
          }
          if (flag) {
            this.history.push({
              'speaker': 'robot',
              'type': 'stop-app',
              'data': errorMessage
            })
          } else {
            this.history.push({
              'speaker': 'robot',
              'type': 'error-message',
              'showError': false,
              'data': errorMessage
            })
          }
          console.log('执行单步task失败：', error.response.data)
          this.errorMessage = true
        })
      } catch (error) {
        console.log('获取MemoryBus失败:', error)
      }

      this.$nextTick(() => {
        this.scrollToInput()
      })
    },
    cancelExecution() {
      let errorMessage = ''
      errorMessage = '用户主动退出了应用'
      this.history.push({
        'speaker': 'robot',
        'type': 'stop-app',
        'data': errorMessage
      })
      this.errorMessage = true
      this.inputDialogVisible = false
    },
    /* 跳转到当前应用的详情页 */
    toDetail() {
      this.$router.push({ path: '/app_management/index/details', query: { id: this.appId }})
    },
    /* 跳转到当前应用的日志查询页 */
    toLog() {
      const queryId = this.appId
      this.$router.push({ path: '/app_management/index/details', query: { id: queryId, radio: 1 }})
    },
    /* 跳转到当前应用的编辑页面 */
    toEdit() {
      const queryId = this.appId
      this.$router.push({ path: '/app_management/index/details/edit', query: { id: queryId }})
    },
    async showChart() {
      await getExecutroMemoryBus().then(res => {
        const results = res.yf_processed_output.result
        // console.log('执行结果', typeof results)
        this.waitingTaskList = false
        if (results && results.length > 0 && (typeof results === Array || typeof results === 'object')) {
          results.forEach((item, index) => {
            // console.log('第111111111111111111111个', item.type)
            if (item.type === 'echart' && item.data) {
              this.history.push({
                'speaker': 'robot',
                'type': 'echart',
                'data': item.data
              })
            } else if (item.type === 'text' && item.data) {
              this.history.push({
                'speaker': 'robot',
                'type': 'text',
                'data': item.data
              })
            } else if (item.type === 'file') {
              this.history.push({
                'speaker': 'robot',
                'type': 'file',
                'data': item.data
              })
            } else if (item.type === 'video') {
              this.history.push({
                'speaker': 'robot',
                'type': 'video',
                'data': item.data
              })
            }
            // 如果不是第一个元素，添加notDisplayAvatar: false
            if (index > 0) {
              // console.log('gfsdgsdf:', this.history)
              this.history[this.history.length - 1].notDisplayAvatar = 'none'
            }
          })
        } else {
          // console.warn('No chart option available.')
          // this.$message.error('No chart option available: "' + results + '"')
        }
      }).catch(error => {
        console.log('processOutput出错:', error)
      })
      this.nowOnTask += 1
    },
    scrollToCompletion() {
      this.$nextTick(() => {
        window.scrollTo({
          top: document.body.scrollHeight, // 页面底部的绝对位置
          behavior: 'smooth' // 平滑滚动
        })
      })
    },
    scrollToInput() {
      this.$nextTick(() => {
        // Ensure that DOM updates have completed before trying to scroll
        const formItem = this.$el.querySelector('.focus-bottom:last-of-type')
        if (formItem) {
          const top = formItem.getBoundingClientRect().top + window.scrollY
          window.scrollTo({
            top,
            behavior: 'smooth' // Smooth scrolling
          })
        } else {
          console.error('Form item not found')
        }
      })
    },
    toggleErrorLog(item) {
      item.showError = !item.showError
    },
    getImageId(appName) {
      if (!appName || appName.length === 0) return 0
      const firstChar = appName[0].toUpperCase() // 取应用名称的首字母并转为大写
      const charCode = firstChar.charCodeAt(0) // 转换为字符编码
      return charCode % 20 // 对20取模，得到0-19
    },
    // 获取图片URL
    getImageUrl() {
      return require(`@/assets/icons/${this.imageId}.png`)
    },
    /* 文件相关 */
    // 上传文件的请求发送函数
    uploadFile(param, variableName) {
      this.file = param.file
      this.formData = new FormData()

      // 如果 param.file 是单个文件，将其转换为数组
      const files = Array.isArray(param.file) ? param.file : [param.file]

      // 遍历文件数组，将每个文件添加到 FormData 中，并记录文件名
      files.forEach(file => {
        // 获取系统时间（精确到秒）
        const timestamp = new Date().toISOString().replace(/[:\-]/g, '').substring(0, 17); // 格式化为 YYYYMMDDTHHMMSS

        // 修改文件名：原文件名 + 系统时间
        const originalFileName = file.name
        const extension = originalFileName.split('.').pop() // 获取文件扩展名
        const newFileName = `${originalFileName.split('.')[0]}_${timestamp}.${extension}`

        // 将文件名添加到 fileName 数组中
        this.fileName.push(newFileName)

        // 将 variableName 和 newFileName 的映射存储到 fileMap 中
        this.fileMap[variableName] = newFileName

        // 创建一个新文件对象，修改文件名
        const newFile = new File([file], newFileName, { type: file.type })

        // 将新文件添加到 FormData 中
        this.formData.append('file', newFile)
      })

      // // 遍历文件数组，将每个文件添加到 FormData 中，并记录文件名
      // files.forEach(file => {
      //   this.fileName.push(file.name) // 将文件名添加到 fileName 数组中
      //   this.formData.append('file', file) // 将文件添加到 FormData 中
      // })
      this.upload_percentage = 0 // 重置进度
      this.on_uploading = true

      uploadFile(this.formData, (progressEvent) => {
        // 每次进度事件触发时，更新进度信息
        // console.log('实时进度:', progressEvent.loaded);
        // console.log('总进度:', progressEvent.total);
        const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        this.upload_percentage = percentCompleted;
        // alert(this.upload_percentage); // 注意：alert 会阻塞代码执行，不建议在实际应用中使用
      });

      // 设置定时器，每100ms检查一次进度
      const intervalId = setInterval(() => {
        // 模拟进度更新逻辑
        if (this.upload_percentage < 100) {
          // console.log(`当前进度: ${this.upload_percentage}%`);
        } else {
          // 如果进度达到100%，清除定时器
          clearInterval(intervalId);
          // console.log('上传完成');
        }
      }, 1) // 100ms触发一次，即每秒10次

      // 上传完成或失败时清除定时器
      Promise.race([
        uploadFile(this.formData).then(response => {
          clearInterval(intervalId); // 清除定时器
          console.log('上传文件成功', response);
          // 保存二进制格式的文件
          this.fileData = response;
          this.$emit('save-file', response);
          // 文件图标设为绿色
          param.onSuccess()
          this.on_uploading = false // 上传完成，隐藏进度条
        }),
        uploadFile(this.formData).catch(error => {
          clearInterval(intervalId) // 清除定时器
          console.log('上传文件失败', error)
          // param.onError();
          this.on_uploading = false // 上传失败，隐藏进度条
        })
      ])
      // // 发送上传请求
      // uploadFile(this.formData, (progressEvent) => {
      //   console.log('实时进度:',progressEvent.loaded)
      //   console.log('总进度:',progressEvent.total)
      //   const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
      //   this.upload_percentage = percentCompleted;
      //   alert(this.upload_percentage)
      // })
      //   .then(response => {
      //     console.log('上传文件成功', response)
      //     // 保存二进制格式的文件
      //     this.fileData = response
      //     this.$emit('save-file', response)
      //     // 文件图标设为绿色
      //     param.onSuccess()
      //     this.on_uploading = false // 上传完成，隐藏进度条
      //   })
      //   .catch(error => {
      //     console.log('上传文件失败', error)
      //     // param.onError();
      //     this.on_uploading = false // 上传失败，隐藏进度条
      //   })
    },
    // 移除上传文件的处理函数
    beforeRemoveFile(file, fileList) {
      if (this.removeConfirm) {
        return this.$confirm(`${this.$t('sure_remove_tip')}${file.name}?`)
      } else {
        this.removeConfirm = true
        return
      }
    },
    // 上传文件个数超过限制的处理函数
    handleExceed(files, fileList) {
      this.$message.warning(`${this.$t('file_upload_tip_one')}，${this.$t('file_upload_tip_two')} ${files.length} ${this.$t('file_upload_tip_four')}，${this.$t('file_upload_tip_three')} ${files.length + fileList.length} ${this.$t('file_upload_tip_four')}`)
    },
    handleSuccess(response, file, fileList, index) {
      // 更新对应的 fileList
      this.fileLists[index] = fileList
      console.log('上传成功后的 fileLists', this.fileLists)
    },
    beforeUpload(file, fileSizeLimit, fileExtensions) {
      // Check file type
      const allowedExtensions = fileExtensions.split(',').map(ext => ext.trim())
      const fileExtension = file.name.split('.').pop().toLowerCase()

      // console.log('比对文件类型', allowedExtensions, fileExtension)

      if (!allowedExtensions.includes(`${fileExtension}`)) {
        this.$message.error(`文件类型不支持，仅支持 ${fileExtensions} 格式`)
        this.removeConfirm = false
        return false
      }

      // Check file size
      const fileSize = file.size
      console.log('比较文件大小', fileSize, fileSizeLimit)
      if (fileSize > (fileSizeLimit * 1024 * 8)) {
        this.$message.error(`文件大小不能超过 ${fileSizeLimit}kB`)
        this.removeConfirm = false
        return false
      }

      return true
    },
    // handleSuccess(response, file, fileList) {
    //   this.fileList = fileList
    // },
    createImageFromBinaryStream(base64String) {
      // 将Base64字符串解码为二进制数据
      const binaryStream = atob(base64String) // atob() 用于解码Base64字符串
      const len = binaryStream.length
      const bytes = new Uint8Array(len)
      for (let i = 0; i < len; i++) {
        bytes[i] = binaryStream.charCodeAt(i)
      }
      // 将二进制流转换为Blob对象
      const blob = new Blob([bytes], { type: 'image/png' }) // 根据实际图片类型设置MIME类型

      // 创建一个临时URL
      const imageUrl = URL.createObjectURL(blob)

      // 创建img元素
      self.img = document.createElement('img')
      self.img.src = imageUrl

      // 设置最大宽度和最大高度
      self.img.style.maxWidth = '100%'
      self.img.style.maxHeight = '500px'

      // 设置object-fit属性
      self.img.style.objectFit = 'contain'

      // 设置height属性
      self.img.style.height = 'auto'

      // 将img元素添加到特定的div中
      // const imageContainer = document.getElementById('image_load')
      // imageContainer.appendChild(self.img)

      // 创建下载链接
      self.downloadLink = document.createElement('a')
      self.downloadLink.href = imageUrl
      self.downloadLink.download = 'downloaded_image.png' // 指定下载的文件名
      self.downloadLink.textContent = '下载'

      // 将下载链接添加到DOM中
      // imageContainer.appendChild(self.downloadLink)

      // 在不需要时释放URL对象
      // URL.revokeObjectURL(imageUrl);
    },
    getOringalImg(file) {
      return URL.createObjectURL(file)
    },
    downloadUrl(file) {
      return URL.createObjectURL(file)
    },
    // 重新执行
    restartExecution() {
      // 清空执行信息
      this.nowOnTask = 0
      this.nowTaskArgs = null
      this.nowTaskContent = null
      this.args = null
      this.step = null
      // 清空文件信息
      this.fileLists = {}
      this.file = null
      this.fileData = null
      this.formData = null
      this.img = null
      this.downloadLink = null
      this.fileName = []

      // 清空状态信息
      this.memoryBus = null
      this.inputDialogVisible = false
      this.waitingTaskList = false
      this.errorMessage = false
      this.getDetailAndShow() // 重新获取应用详情并初始化
    },
    // 多选后需要强制刷新（重新渲染）
    handleMultiSelect() {
      this.$forceUpdate()
    },
    /////////////// app icon /////////////////
    async loadIcon() {
      try {
        const response = await getIconByAppId(this.appId);

        if (response.type.startsWith('image/')) {
          const blob = await response
          const url = URL.createObjectURL(blob)
          this.img_src = url

          this.$once('hook:beforeDestroy', () => {
            URL.revokeObjectURL(url)
          })
        } else {
          console.log("The response is not an image type")
          this.img_src = require(`@/assets/icons/0.png`)
        }
      } catch (error) {
        console.log("Failed to load icon")
        console.log(error)
        this.img_src = require(`@/assets/icons/0.png`)
      }
    },
  }
}
</script>

<style scoped>
.user-avatar{
  cursor: pointer;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  margin-bottom: 5px;
}
.content-wrapper{
  width: 100%;
  background-color: white;
  padding: 15px;
}

.history{
  width: 100%;
}
.container-item{
  width: 100%;
  padding: 20px;
}
.dialog-box {
  border-radius: 10px;
}
.button-container {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}
.yuanfang-avatar {
  cursor: pointer;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  margin-bottom: 10px;
}
.item-spacing {
  margin-top: 10px; /* 仅在数组内容之间增加 10px 的间距 */
}

div>span {
  word-wrap: break-word;
}
</style>
